//
//  YD_DataModel.swift
//  YD_DataModel
//
//  Created by alyadan on 2017/3/8.
//  Copyright © 2017年 alyadan. All rights reserved.
//

import UIKit
import YD_NetworkModule
import Alamofire
import RxSwift
import RxCocoa
import ObjectMapper
import SwiftyJSON


public enum DataModelParseError: Error {
    case JsonParseFailed
    case DataKeyNameError
    case ResponseDataError
}

public typealias NormalBlock = () -> Void

public var dataModelNetwork: YDNetwork!

public protocol YDDataModelType: Mappable {}


open class YD_DataModel: YDDataModelType {
    
    /// This function can be used to validate JSON prior to mapping. Return nil to cancel mapping at this point
    public required init?(map: Map) {
        
    }
    
    open class func parseData(data: Any) -> Self? {
        if let data = data as? [String: Any] {
            return self.init(JSON: data)
        }
        return nil
    }
    
    open func mapping(map: Map) {
        
    }
    
    
    open class func parseDataList(data: Any) -> [YD_DataModel]? {
        if let data = data as? [Any] {
            
            var result = [YD_DataModel]()
            for item in data{
                guard let itemDict = item as? [String: Any] else {
                    return nil
                }
                
                if let x = self.parseData(data: itemDict) {
                    result.append(x)
                } else {
                    return nil
                }
            }
            return result
        }
        return nil
        
    }
    
    
    open class func fetchModel(url: String, isList: Bool = false, httpMethod: HTTPMethod = .get, param: [String: Any]? = nil,  header: HTTPHeaders? = nil, dataKeyPath: String = "data") -> Observable<(Any, [String: JSON]?)> {
        
        return Observable.create { (observer: AnyObserver<(Any, [String: JSON]?)>) -> Disposable in
            
            let dispose = dataModelNetwork.request(method: httpMethod, param: param, url: url, header: header)
                .subscribe(onNext: { (data: ([String : Any], origin: [String : JSON]?)) in
                    
                    let origin1 = data.1!.map({  (key: String, value: JSON) -> (String, Any) in
                        (key, value.object)
                    })
                    let originDict = Dictionary.init(uniqueKeysWithValues: origin1)
                    
                    var expectData: Any = 0
                    
                    if let respData: Any = isList ? data.0.getArrayValue(keyPath: dataKeyPath) : originDict.getValue(keyPath: dataKeyPath) {
                        expectData = respData
                    } else {
                        observer.onError(DataModelParseError.DataKeyNameError)
                    }
                    
                    if let parsedData: Any =  isList ? self.parseDataList(data: expectData): self.parseData(data: expectData){
                        observer.onNext((parsedData, data.1))
                    }else{
                        observer.onError(DataModelParseError.JsonParseFailed)
                    }
                }, onError: { (error) in
                    observer.onError(error)
                }, onCompleted: {
                    observer.onCompleted()
                }, onDisposed: nil)
            
            return dispose
        }
        .share(replay: 1)
    }
}

open class YD_DataModel_OC: NSObject, YDDataModelType {
    
    /// This function can be used to validate JSON prior to mapping. Return nil to cancel mapping at this point
    public required init?(map: Map) {
        
    }
    
    
    open func mapping(map: Map) {
        
    }
    
    open class func parseData(data: Any) -> Self? {
        if let data = data as? [String: Any] {
            return self.init(JSON: data)
        }
        return nil
    }
    
    open class func parseDataList(data: Any) -> [YD_DataModel_OC]? {
        if let data = data as? [Any] {
            
            var result = [YD_DataModel_OC]()
            for item in data{
                guard let itemDict = item as? [String: Any] else {
                    return nil
                }
                
                if let x = self.parseData(data: itemDict) {
                    result.append(x)
                } else {
                    return nil
                }
            }
            return result
        }
        return nil
        
    }
    
    
    open class func fetchModel(url: String, isList: Bool = false, httpMethod: HTTPMethod = .get, param: [String: Any]? = nil,  header: HTTPHeaders? = nil, dataKeyPath: String = "data") -> Observable<(Any, [String: JSON]?)> {
        
        return Observable.create { (observer: AnyObserver<(Any, [String: JSON]?)>) -> Disposable in
            
            let dispose = dataModelNetwork.request(method: httpMethod, param: param, url: url, header: header)
                .subscribe(onNext: { (data: ([String : Any], origin: [String : JSON]?)) in
                    
                    let origin1 = data.1!.map({  (key: String, value: JSON) -> (String, Any) in
                        (key, value.object)
                    })
                    let originDict = Dictionary.init(uniqueKeysWithValues: origin1)
                    var expectData: Any = 0
                    
                    if let respData: Any = isList ? data.0.getArrayValue(keyPath: dataKeyPath) : originDict.getValue(keyPath: dataKeyPath) {
                        expectData = respData
                    } else {
                        observer.onError(DataModelParseError.DataKeyNameError)
                    }
                    
                    if let parsedData: Any =  isList ? self.parseDataList(data: expectData): self.parseData(data: expectData){
                        observer.onNext((parsedData, data.1))
                    }else{
                        observer.onError(DataModelParseError.JsonParseFailed)
                    }
                }, onError: { (error) in
                    observer.onError(error)
                }, onCompleted: {
                    observer.onCompleted()
                }, onDisposed: nil)
            
            return dispose
        }
        .share(replay: 1)
    }
    
    @objc
    public static func fetchModelOC(url: String, isList: Bool = false, httpMethod: String = "get", param: [String : Any]? = nil, header: HTTPHeaders? = nil, dataKeyPath: String = "data", successBlock: @escaping (Any, [String: Any]?) -> Void, failuar: ((Error) -> Void)?) {
        
        _ = self.fetchModel(url: url, isList: isList, httpMethod: HTTPMethod.init(rawValue: httpMethod.uppercased()) ?? .get, param: param, header: header, dataKeyPath: dataKeyPath)
            .subscribe(onNext: { (data: (Any, [String : JSON]?)) in
                let json = JSON(data.1 as Any)
                successBlock(data.0, json.dictionaryObject)
            }, onError: { (err) in
                failuar?(err)
            }, onDisposed: {
                failuar?(NSError.init(domain: "request canceled", code: 400, userInfo: nil))
            })
    }
    
}
